<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="qKYJk" id="qKYJk"><span data-lake-id="u8924775d" id="u8924775d">典型回答</span></h1>
  <p data-lake-id="u1d8c4688" id="u1d8c4688"><br></p>
  <p data-lake-id="u4500ddba" id="u4500ddba"><span data-lake-id="u21240fb2" id="u21240fb2">RabbitMQ的死信队列（Dead Letter Queue，简称DLQ）是一种用于处理消息处理失败或无法路由的消息的机制。它允许将无法被正常消费的消息重新路由到另一个队列，以便稍后进行进一步的处理、分析或排查问题。</span></p>
  <p data-lake-id="ufed5eb49" id="ufed5eb49"><span data-lake-id="uefb4e9be" id="uefb4e9be">​</span><br></p>
  <p data-lake-id="u53714792" id="u53714792"><span data-lake-id="u8d12fd13" id="u8d12fd13">当消息队列里面的消息出现以下几种情况时，就可能会被称为"死信"：</span></p>
  <p data-lake-id="u6d844a37" id="u6d844a37"><span data-lake-id="u4dc2d38c" id="u4dc2d38c">​</span><br></p>
  <ol list="ua2271bda">
   <li fid="u984a8aa0" data-lake-id="u7f1b3642" id="u7f1b3642"><span data-lake-id="u611f0382" id="u611f0382">消息处理失败：当消费者由于代码错误、消息格式不正确、业务规则冲突等原因无法成功处理一条消息时，这条消息可以被标记为死信。</span></li>
   <li fid="u984a8aa0" data-lake-id="u9117519a" id="u9117519a"><span data-lake-id="u9796ed52" id="u9796ed52">消息过期</span><span data-lake-id="u1ee5ceba" id="u1ee5ceba">：在RabbitMQ中，消息可以设置过期时间。如果消息在规定的时间内没有被消费，它可以被认为是死信并被发送到死信队列。</span></li>
   <li fid="u984a8aa0" data-lake-id="u5927ee0e" id="u5927ee0e"><span data-lake-id="u1d28b926" id="u1d28b926">消息被拒绝</span><span data-lake-id="u3be974c2" id="u3be974c2">：当消费者明确拒绝一条消息时，它可以被标记为死信并发送到死信队列。拒绝消息的原因可能是消息无法处理，或者消费者认为消息不符合处理条件。</span></li>
   <li fid="u984a8aa0" data-lake-id="u095785cd" id="u095785cd"><span data-lake-id="u157ce049" id="u157ce049">消息无法路由：当消息不能被路由到任何队列时，例如，没有匹配的绑定关系或路由键时，消息可以被发送到死信队列。</span></li>
  </ol>
  <p data-lake-id="ufee474b6" id="ufee474b6"><span data-lake-id="ub8ca1c2c" id="ub8ca1c2c">​</span><br></p>
  <p data-lake-id="u249fc955" id="u249fc955"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1693626681115-affdb40a-bbe2-4664-a046-a254a1893d66.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_51%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u0f645338" id="u0f645338"><br></p>
  <p data-lake-id="uf1f110eb" id="uf1f110eb"><span data-lake-id="uaa2b6413" id="uaa2b6413">当消息变成"死信"之后，如果配置了死信队列，它将被发送到死信交换机，死信交换机将死信投递到一个队列上，这个队列就是死信队列。但是如果没有配置死信队列，那么这个消息将被丢弃。</span></p>
  <p data-lake-id="ud87eca97" id="ud87eca97"><span data-lake-id="ude350b6c" id="ude350b6c">​</span><br></p>
  <p data-lake-id="u191d2301" id="u191d2301"><span data-lake-id="uc2ade4f3" id="uc2ade4f3">RabbitMQ的死信队列其实有很多作用，比如我们可以借助他实现延迟消息，进而实现订单的到期关闭，超时关单等业务逻辑。</span></p>
  <p data-lake-id="ua0530f77" id="ua0530f77"><span data-lake-id="ub4e409b6" id="ub4e409b6">​</span><br></p>
  <p data-lake-id="u373b15a4" id="u373b15a4"><br></p>
  <h1 data-lake-id="N0Vng" id="N0Vng"><span data-lake-id="u2e2bc3a4" id="u2e2bc3a4">扩展知识</span></h1>
  <p data-lake-id="ua3ffbc74" id="ua3ffbc74"><br></p>
  <h2 data-lake-id="URikJ" id="URikJ"><span data-lake-id="u3560fc10" id="u3560fc10">配置死信队列</span></h2>
  <p data-lake-id="u02ded67b" id="u02ded67b"><br></p>
  <p data-lake-id="ufd9496f7" id="ufd9496f7"><br></p>
  <p data-lake-id="u16898df6" id="u16898df6"><span data-lake-id="u4d279b7f" id="u4d279b7f">在RabbitMQ中，死信队列通常与交换机（Exchange）和队列（Queue）之间的绑定关系一起使用。要设置死信队列，通常需要以下步骤：</span></p>
  <p data-lake-id="uafdb9540" id="uafdb9540"><span data-lake-id="u2ce21861" id="u2ce21861">​</span><br></p>
  <ol list="uc0e5f3e7">
   <li fid="u9e8ac131" data-lake-id="u15792cc1" id="u15792cc1"><span data-lake-id="u9833ccd9" id="u9833ccd9">创建死信队列：定义一个用于存储死信消息的队列。</span></li>
   <li fid="u9e8ac131" data-lake-id="u8896a3db" id="u8896a3db"><span data-lake-id="ua178e60b" id="ua178e60b">创建死信交换机：为死信队列定义一个交换机，通常是一个direct类型的交换机。</span></li>
   <li fid="u9e8ac131" data-lake-id="uf2cc3027" id="uf2cc3027"><span data-lake-id="u094b7dec" id="u094b7dec">将队列与死信交换机绑定：将主要队列和死信交换机绑定，以便无法处理的消息能够被转发到死信队列。</span></li>
   <li fid="u9e8ac131" data-lake-id="u7199d546" id="u7199d546"><span data-lake-id="u8e4e5e0e" id="u8e4e5e0e">在主要队列上设置死信属性：通过设置队列的</span><span data-lake-id="u4f518eec" id="u4f518eec">x-dead-letter-exchange</span><span data-lake-id="uebe3bf2f" id="uebe3bf2f">和</span><span data-lake-id="u1007922a" id="u1007922a">x-dead-letter-routing-key</span><span data-lake-id="u33128e1e" id="u33128e1e">属性，指定死信消息应该被发送到哪个交换机和路由键。</span></li>
  </ol>
  <p data-lake-id="uee476714" id="uee476714"><span data-lake-id="u5c2b556a" id="u5c2b556a">当消息被标记为死信时，它将被发送到死信队列，并可以由应用程序进一步处理、审查或记录。这种机制有助于增加消息处理的可靠性和容错性，确保不丢失重要的消息，并提供了一种处理失败消息的方式。</span></p>
  <p data-lake-id="u61066169" id="u61066169"><span data-lake-id="u9bb56b4d" id="u9bb56b4d">​</span><br></p>
  <p data-lake-id="u7cde7cfb" id="u7cde7cfb"><span data-lake-id="ue2d2c8c7" id="ue2d2c8c7">以下是一个配置死信队列的方式：</span></p>
  <p data-lake-id="u68f7c6ee" id="u68f7c6ee"><span data-lake-id="ua1b2b333" id="ua1b2b333">​</span><br></p>
  <pre lang="java"><code>
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

    // 配置死信队列和交换机
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange("dead-letter-exchange");
    }

    // 死信队列
    @Bean
    public Queue deadLetterQueue() {
        return new Queue("dead-letter-queue");
    }

    // 绑定死信队列到死信交换机
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with("dead-letter-routing-key");
    }

    // 主队列的交换机
    @Bean
    public DirectExchange mainExchange() {
        return new DirectExchange("main-exchange");
    }

 		// 主队列
    @Bean
    public Queue mainQueue() {
      	Map&lt;String, Object&gt; args = new HashMap&lt;&gt;(2);
      	// 声明当前队列绑定的死信交换机 
				args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
      	// 这里声明当前队列的死信路由key 
				args.put("x-dead-letter-routing-key", "dead-letter-routing-key");

      	return QueueBuilder.durable("main-queue").withArguments(args).build();
    }


    // 绑定主队列到主交换机
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(mainQueue()).to(mainExchange()).with("main-routing-key");
    }
}

</code></pre>
  <p data-lake-id="u7d3ce1c8" id="u7d3ce1c8"><span data-lake-id="uc762175c" id="uc762175c">​</span><br></p>
  <p data-lake-id="uf58a7a02" id="uf58a7a02"><span data-lake-id="ud1d66c53" id="ud1d66c53">​</span><br></p>
  <p data-lake-id="u9fd8a5a8" id="u9fd8a5a8"><span data-lake-id="uece0eb80" id="uece0eb80">这样，消费者在消费的时候，分别监听主队列和死信队列就可以了：</span></p>
  <p data-lake-id="ufe01ed27" id="ufe01ed27"><span data-lake-id="ub20e8fb5" id="ub20e8fb5">​</span><br></p>
  <pre lang="java"><code>
@Component 
public class DeadLetterMessageReceiver { 
    @RabbitListener(queues = "dead-letter-queue") 
    public void receiveA(Message message, Channel channel) throws IOException { 
      System.out.println("收到死信消息：" + new String(message.getBody())); 
      channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); 
    } 
}


@Component 
public class MainMessageReceiver { 
    @RabbitListener(queues = "main-queue") 
    public void receiveA(Message message, Channel channel) throws IOException { 
      System.out.println("收到普通消息A：" + new String(message.getBody())); 
      channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); 
    } 
}
</code></pre>
 </body>
</html>